/*
 * pldtc.c
 *
 *  Created on: 2016Äê3ÔÂ13ÈÕ
 *      Author: huangkaibin
 */

#include "costr.h"
#include "cobit.h"
#include "pldtc.h"
#include "plfault_monitor.h"
#include "pleep.h"
#include "pleep_config.h"
#include "Eep_DFlash.h"
#include "pleep.h"
#include "plconfig.h"
#include "plcan.h"

#if PLCFG_DTC_ENABLED == 1

#define PLDTC_TOTAL_MAX_RECORD_COUNT (100U)

PlDtcSnapRecordStatus pldtc_snapshot_record_status_cache[PLDTC_TOTAL_MAX_RECORD_COUNT];

uint16 pldtc_snap_buffer_request_error_count = 0U;
bool   pldtc_snap_buffer_request_failed = FALSE;

uint8 pldtc_eep_mirror_config_index;
uint8 pldtc_eep_snapshot_config_index;
bool  pldtc_suppress_updating_dtc = FALSE;

static uint16 pldtc_current_snap_record_data_size_index = 0U;
static uint16 pldtc_current_snap_eep_offset = 0U;

PlDtcSnapBuffer pldtc_snap_buffer_pool[SNAP_BUFFER_COUNT];

static void pldtc_write_eep_mirror(uint8 dtc_config_index);
static void pldtc_load_eep_mirror(uint8 dtc_config_index);
static uint8 pldtc_get_proper_snap_buffer(uint8 dtc_config_index);
static uint8 pldtc_get_record_index_from_record_number(uint8 dtc_config_index, uint8 record_number);

static void pldtc_load_eep_mirror(uint8 dtc_config_index) {
	bool load_result;
	uint16 eep_size = sizeof(PlDtcEepMirror);
	uint16 eep_address_offset = eep_size * dtc_config_index;
	PlDtcEepMirror *mirror_ram = &(pldtc_eep_mirror[dtc_config_index]);
	load_result = pleep_load_to_ram_with_offset(pldtc_eep_mirror_config_index, eep_address_offset, eep_size, (uint8 *)mirror_ram, FALSE);
	if(load_result == FALSE) {
		memory_clear((uint8*)mirror_ram, eep_size);
	}
	pldtc_info[dtc_config_index].eep_load_result = load_result;
	mirror_ram->dtc_status.status_bits.testFailed = false;
	mirror_ram->dtc_status.status_bits.testFailedThisOperationCycle = false;
	mirror_ram->dtc_status.status_bits.testNotCompletedThisOperationCycle = true;
}

static void pldtc_write_eep_mirror(uint8 dtc_config_index) {
	uint16 eep_size = sizeof(PlDtcEepMirror);
	uint16 eep_address_offset = eep_size * dtc_config_index;
	PlDtcEepMirror *mirror_ram = &(pldtc_eep_mirror[dtc_config_index]);
	(void)pleep_write_back_data_with_offset(pldtc_eep_mirror_config_index, eep_address_offset, eep_size, (uint8 *)mirror_ram);
}


static void pldtc_init_did_info(uint8 dtc_config_index) {
	const PlDtcConfig *dtc_config = &(pldtc_dtc_config[dtc_config_index]);
	const PlDidRecordList dtc_record_list = dtc_config->record_list;
	PlDtcInfo *dtc_info = &(pldtc_info[dtc_config_index]);
	uint8 snap_record_count = 0U;
	PlDidRecord did_record;
	uint8 index = 0U;
	uint8 did_count;
	uint16 snap_record_size;
	uint16 snap_record_data_offset = 0U;
	uint16 total_snap_data_size = 0U;
	bool should_align_4byte = FALSE;
	uint8 reminder_offset;
	bool should_include_did_number = TRUE;

	dtc_info->eep_clear_state = DTC_EEP_CLEAR_FINISHED;
	dtc_info->snap_record_offset = pldtc_current_snap_record_data_size_index;
	dtc_info->snap_eep_address_offset = pldtc_current_snap_eep_offset;
	if(dtc_record_list != NULL) {
		while(TRUE) {
			did_record = pldid_get_did_record(dtc_record_list, index++);
//			did_record = dtc_record_list[index++];
			if(did_record == NULL) {
				break ;
			}
			snap_record_count++;
			/*did number will also be included to count the record size.*/
			snap_record_size = pldid_get_record_size(did_record, &did_count, should_include_did_number);
			total_snap_data_size += snap_record_size;
			pldtc_snapshot_record_status_cache[pldtc_current_snap_record_data_size_index].record_data_size = (uint8)snap_record_size;
			pldtc_snapshot_record_status_cache[pldtc_current_snap_record_data_size_index].record_data_offset = (uint8)snap_record_data_offset;
			pldtc_snapshot_record_status_cache[pldtc_current_snap_record_data_size_index].record_did_count = did_count;
			snap_record_data_offset += (snap_record_size + EEP_BYTE_COUNT_FOR_MARK_CRC);
			reminder_offset = (snap_record_data_offset % EEP_SECTION_SIZE);
			if(should_align_4byte && (reminder_offset > 0U)) {
				snap_record_data_offset += (EEP_SECTION_SIZE - reminder_offset);
			}

			pldtc_current_snap_record_data_size_index++;
		}
	}
	pldtc_current_snap_eep_offset += snap_record_data_offset;
	dtc_info->snap_record_count = snap_record_count;
	dtc_info->total_snap_data_size = total_snap_data_size;
}


void pldtc_monitor_init(void) {
	uint8 indexi;
	uint32 dtc_code;
	uint8 target_index;
	PlDtcInfo *dtcinfo;

	memory_clear((uint8 *)pldtc_snapshot_record_status_cache, sizeof(pldtc_snapshot_record_status_cache));
	memory_clear((uint8*)pldtc_snap_buffer_pool, sizeof(pldtc_snap_buffer_pool));

	for(indexi = 0U; indexi < pldtc_config_count; indexi++) {
		dtcinfo = &(pldtc_info[indexi]);
		dtcinfo->monitor_index = INVALID_MONITOR_INDEX;
		dtcinfo->enabled = (dtc_code != INVALID_DTC_CODE);
		dtc_code = pldtc_dtc_config[indexi].dtc_code;
		pldtc_init_did_info(indexi);
	}

	for(indexi = 0U; indexi < plfault_monitor_config_count; indexi++) {
		dtc_code = plfault_monitor_config[indexi].dtc_code;
		target_index = pldtc_find_dtc_config_index(dtc_code);
		plfault_monitor_dtc_index_mapping[indexi] = target_index;

		dtcinfo = &(pldtc_info[target_index]);
		if(target_index != INVALID_DTC_CONFIG_INDEX) {
			dtcinfo->monitor_index = indexi;
		} else {
			dtcinfo->enabled = FALSE;
		}
	}

	for(indexi = 0U; indexi < pldtc_config_count; indexi++) {
		pldtc_load_eep_mirror(indexi);
	}

}

static uint8 pldtc_get_proper_snap_buffer(uint8 dtc_config_index) {
	uint8 index;
	uint8 buffer_index = INVALID_SNAP_BUFFER_INDEX;
	uint8 unused_buffer_index = INVALID_SNAP_BUFFER_INDEX;
	const PlDtcSnapBuffer *snap_buffer;
	bool acquire_new = FALSE;
	for(index = 0U; index < SNAP_BUFFER_COUNT; index++) {
		snap_buffer = &(pldtc_snap_buffer_pool[index]);
		if(snap_buffer->assigned_dtc_config_index == dtc_config_index) {
			buffer_index = index;
			break ;
		}
		if(snap_buffer->buffer_acquired == FALSE) {
			unused_buffer_index = index;
		}
	}
	if(buffer_index == INVALID_SNAP_BUFFER_INDEX) {
		buffer_index = unused_buffer_index;
		acquire_new = TRUE;
	}
	if(buffer_index != INVALID_SNAP_BUFFER_INDEX) {
		PlDtcSnapBuffer *snap_buffer = &(pldtc_snap_buffer_pool[buffer_index]);
//		if(os_running){Cpu_lock();}else{}
		drsys_lock_cpu();
		snap_buffer->assigned_dtc_config_index = dtc_config_index;
		snap_buffer->buffer_acquired = TRUE;
		if(acquire_new) {
			snap_buffer->record_updated_bits = 0U;
			snap_buffer->record_saved_bits = 0U;
		}
//		if(os_running){Cpu_unlock();}else{}
		drsys_unlock_cpu();
		pldtc_snap_buffer_request_failed = FALSE;
	} else {
		pldtc_snap_buffer_request_error_count++;
		pldtc_snap_buffer_request_failed = TRUE;
	}

	return buffer_index;
}

static uint8 pldtc_get_record_index_from_record_number(uint8 dtc_config_index, uint8 record_number) {
	const PlDtcConfig *dtc_config = &(pldtc_dtc_config[dtc_config_index]);
	PlDtcInfo *dtc_info = &(pldtc_info[dtc_config_index]);
	uint8 index;
	uint8 record_number_index = INVALID_RECORD_INDEX;
	for(index = 0U; index < dtc_info->snap_record_count; index++) {
		if(pldtc_get_record_number_from_dtc_config(dtc_config, index) == record_number) {
			record_number_index = index;
			break ;
		}
	}
	return record_number_index;
}


uint8 pldtc_find_dtc_config_index(uint32 dtc_code) {
	uint8 index;
	uint8 dtc_config_index = INVALID_DTC_CONFIG_INDEX;
	for(index = 0U; index < pldtc_config_count; index++) {
		if(pldtc_dtc_config[index].dtc_code == dtc_code) {
			dtc_config_index = index;
			break ;
		}
	}
	return dtc_config_index;
}

void pldtc_enter_sleep(void) {
	uint8 index;
	PlDtcEepMirror *mirror_ram;
	bool test_passed;
	uint8 dtc_aging_count_old;
	for(index = 0U; index < pldtc_config_count; index++) {
		mirror_ram = &(pldtc_eep_mirror[index]);
		dtc_aging_count_old = mirror_ram->dtc_aging_count;
		test_passed = (mirror_ram->dtc_status.status_bits.testNotCompletedThisOperationCycle == false)
	    	        && (mirror_ram->dtc_status.status_bits.testFailedThisOperationCycle == false);
		if(test_passed) {
			mirror_ram->dtc_status.status_bits.pendingDTC = false;
			if (mirror_ram->dtc_aging_count < pldtc_dtc_config[index].dtc_aging_thresold) {
				mirror_ram->dtc_aging_count++;
			}
			mirror_ram->fault_pending_count = 0U;
			if(mirror_ram->dtc_aging_count == pldtc_dtc_config[index].dtc_aging_thresold
					&& dtc_aging_count_old < pldtc_dtc_config[index].dtc_aging_thresold) {
				if(mirror_ram->dtc_aged_count < 0xFFU) {
					mirror_ram->dtc_aged_count++;
				}
				pldtc_request_clear_diag(index, true);
			}
	    }
	}
	pldtc_wait_until_dtc_eep_completed();
}

void pldtc_wait_until_dtc_eep_completed(void) {
	bool saved;
	bool cleared;
	uint8 index;
	PlDtcInfo *dtc_info;
	/* save not clear DTC */
	for(index = 0U; index < pldtc_config_count; index++) {
		dtc_info = &(pldtc_info[index]);
		if (dtc_info->eep_clear_state != DTC_EEP_CLEAR_REQUESTED) {
			pldtc_write_eep_mirror(index);
		}
	}
	/* save cleared DTC */
	while(TRUE) {
		saved = pldtc_save_snapshot();
		if(!saved) {
			cleared = pldtc_clear_diag();	//true: clearing; false: clear finished
			if(!cleared) {
				break ; /*all finished*/
			}
		}
	}
}


void pldtc_request_clear_diag(uint8 dtc_config_index, bool clear_due_aged) {
	PlFaultTestingStatus *testing_status = NULL;
	PlDtcEepMirror *eep_mirror;
	PlDtcInfo *dtc_info;
	uint8 monitor_config_index;
	PlDtcStatus dtc_status_old;
	uint8 dtc_aged_count_old;
	uint8 dtc_aging_count_old;

	eep_mirror = &(pldtc_eep_mirror[dtc_config_index]);
	dtc_info = &(pldtc_info[dtc_config_index]);
	monitor_config_index = pldtc_info[dtc_config_index].monitor_index;

	if(monitor_config_index != INVALID_MONITOR_INDEX) {
		testing_status =  &(plfault_testing_status[monitor_config_index]);
	}
	if(testing_status != NULL) {
		testing_status->testing_result.testing_result = 0U;
	}

	dtc_status_old = eep_mirror->dtc_status;
	dtc_aged_count_old = eep_mirror->dtc_aged_count;
	dtc_aging_count_old = eep_mirror->dtc_aging_count;
	memory_clear((uint8 *)eep_mirror, sizeof(PlDtcEepMirror));

	eep_mirror->dtc_status.status_bits.testNotCompletedThisOperationCycle = 1U;

	if(clear_due_aged) {
		eep_mirror->dtc_status.status_bits.testFailedSinceLastClear = dtc_status_old.status_bits.testFailedSinceLastClear;
		eep_mirror->dtc_status.status_bits.testNotCompletedSinceLastClear = dtc_status_old.status_bits.testNotCompletedSinceLastClear;
		eep_mirror->dtc_aged_count = dtc_aged_count_old;
		eep_mirror->dtc_aging_count = dtc_aging_count_old;
	}
	else {
		eep_mirror->dtc_status.status_bits.testNotCompletedSinceLastClear = 1U;
		eep_mirror->dtc_status.status_bits.testFailedSinceLastClear = 0;
		eep_mirror->dtc_aging_count = 0;
		eep_mirror->dtc_aged_count = 0;
	}

	dtc_info->eep_clear_state = DTC_EEP_CLEAR_REQUESTED; /*no need to lock*/
}

bool pldtc_clear_diag(void) {
	uint8 index;
	PlDtcInfo *dtc_info;
	bool buffer_failed;
	bool cleared = FALSE;
	uint8 eep_clear_state;

	for(index = 0U; index < pldtc_config_count; index++) {
		dtc_info = &(pldtc_info[index]);
		eep_clear_state = dtc_info->eep_clear_state;
		if(eep_clear_state != DTC_EEP_CLEAR_FINISHED) {
			cleared = TRUE; /*not all finished*/
			if(eep_clear_state == DTC_EEP_CLEAR_REQUESTED) {
				(void)pldtc_take_full_snapshot(index, &buffer_failed, TRUE, 0x00U); /*fault_occurring_time: use 0 to clear all records*/
				if(!buffer_failed) {
					pldtc_write_eep_mirror(index);
					dtc_info->eep_clear_state = DTC_EEP_STATUS_CLEARED;
				}
			} else if(eep_clear_state == DTC_EEP_SNAP_CLEARED) {
				dtc_info->eep_clear_state = DTC_EEP_CLEAR_FINISHED;
			}
			break ;
		}
	}
	return cleared;
}

void pldtc_request_clear_all_diag(void) {
	uint8 index;
	for(index = 0U; index < pldtc_config_count; index++) {
		pldtc_request_clear_diag(index, FALSE);
	}
}

/* special for GELY-GE11 */
void plfault_monitor_fault_changed_for_dtc(uint8 monitor_config_index) {
	const PlFaultTestingStatus *testing_status;
	const PlDtcConfig *dtc_config;
	PlDtcEepMirror *eep_mirror;
	PlDtcInfo *dtc_info;
	bool failed;
//	PlcanTxInfo *txinfo;

	uint8 dtc_config_index = plfault_monitor_dtc_index_mapping[monitor_config_index];
	if(dtc_config_index == INVALID_DTC_CONFIG_INDEX) {
		return ;
	}

	dtc_info = &(pldtc_info[dtc_config_index]);
//	txinfo = &(plcan_tx_infos[pldtc_tx_config_index]);

	if(pldtc_suppress_updating_dtc == FALSE && dtc_info->eep_clear_state == DTC_EEP_CLEAR_FINISHED) { /*do not update dtc status only updating is suppressed or clear diagnostics is requested*/
		testing_status = &(plfault_testing_status[monitor_config_index]);
		dtc_config = &(pldtc_dtc_config[dtc_config_index]);
		eep_mirror = &(pldtc_eep_mirror[dtc_config_index]);

		failed = testing_status->testing_result.testing_bits.test_failed;
		eep_mirror->dtc_status.status_bits.testFailed = failed;
		eep_mirror->dtc_status.status_bits.testNotCompletedThisOperationCycle = false;
		eep_mirror->dtc_status.status_bits.testNotCompletedSinceLastClear = false;
		if(failed) {
			eep_mirror->dtc_status.status_bits.testFailedSinceLastClear = true;
			eep_mirror->dtc_status.status_bits.pendingDTC = true;
		    eep_mirror->dtc_aging_count = 0U;

			if(eep_mirror->dtc_status.status_bits.testFailedThisOperationCycle == false) {
				if(eep_mirror->fault_pending_count < 0xFFU) {
					eep_mirror->fault_pending_count++;
				}
				eep_mirror->dtc_status.status_bits.testFailedThisOperationCycle = true;
				if(eep_mirror->fault_pending_count >= dtc_config->pending_count_for_confirm) {
					bool buffer_failed;
					if(eep_mirror->dtc_status.status_bits.confirmedDTC == false) {
						eep_mirror->fault_occur_op_cycles = 0x01U;
						/*special for Ge11 ,snapshot only update once*/
					    (void)pldtc_take_full_snapshot(dtc_config_index, &buffer_failed, FALSE, eep_mirror->fault_occur_op_cycles);

					} else if(eep_mirror->fault_occur_op_cycles < 0xFFU) {
						eep_mirror->fault_occur_op_cycles++;
					}
					//eep_mirror->last_fault_odometer =canrx_total_odometer;
					eep_mirror->dtc_status.status_bits.confirmedDTC = true;
				/*	if(eep_mirror->fault_occur_op_cycles <= 0x0FU) { Because only the lower nil-byte of record type is used to specified the fault occurring time, at max only the first 15 faults will be used
						bool buffer_failed;
						(void)pldtc_take_full_snapshot(dtc_config_index, &buffer_failed, FALSE, eep_mirror->fault_occur_op_cycles); TODO:snap_buffer_failed
					}*/
				}
			}
			//eep_mirror->last_fault_odometer =canrx_total_odometer;
		} else {
			/*do nothing*/
		}
	}
}


bool pldtc_should_take_snapshot(uint8 record_type, uint8 fault_occurring_time) {
	bool result = false;
	if((fault_occurring_time == 0x00U) || ((record_type & LATEST_TIME_RECORD) != 0x00U)) {
		result = true;
	} else if(((record_type & EXACT_TIME_RECORD) != 0x00U) && (fault_occurring_time == (record_type & 0x0FU))) {
		result = true;
	}
	return result;
}


bool pldtc_take_snapshot(uint8 dtc_config_index, uint8 record_number, bool *snap_buffer_failed, bool force_use_default, uint8 fault_occurring_time) {
	const PlDtcConfig *dtc_config = &(pldtc_dtc_config[dtc_config_index]);
	const PlDidRecordList dtc_record_list = dtc_config->record_list;
	PlDidRecord did_record;
	uint8 *data_buffer;
	bool result = FALSE;
	bool buffer_request_failed = FALSE;

	if(dtc_record_list != NULL) {
		uint8 snap_buffer_index = pldtc_get_proper_snap_buffer(dtc_config_index);
		buffer_request_failed = (snap_buffer_index == INVALID_SNAP_BUFFER_INDEX);
		if(buffer_request_failed == false) {
			PlDtcInfo *dtc_info = &(pldtc_info[dtc_config_index]);
			PlDtcSnapBuffer *snap_buffer = &(pldtc_snap_buffer_pool[snap_buffer_index]);
			uint8 record_index = 0x00U;
			uint8 end_record_index = dtc_info->snap_record_count;
			if(record_number != RECORD_NUMBER_FOR_ALL) {
				record_index = pldtc_get_record_index_from_record_number(dtc_config_index, record_number);
				end_record_index = record_index + 1U; /*this will over flow if record_index is invalid!!*/
			}

			for(; record_index < end_record_index; record_index++) {
				uint8 buffer_offset;
				uint8 record_type;
				did_record = dtc_record_list[record_index];
				record_type = pldid_get_did_record_type(did_record);
				if(!pldtc_should_take_snapshot(record_type, fault_occurring_time)) {
					continue ;
				}

				buffer_offset = pldtc_snapshot_record_status_cache[dtc_info->snap_record_offset + record_index].record_data_offset;
				data_buffer = (snap_buffer->data + buffer_offset + EEP_BYTE_COUNT_FOR_MARK); /*byte0: 0x55, byte1 0xAA, filled by eep framework*/

				(void)pldid_read_did_record(did_record, FALSE, TRUE, data_buffer, force_use_default);
				snap_buffer->record_updated_bits |= (1U << record_index);
				/*TODO: lock*/
				wordop_bitset_at(snap_buffer->record_updated_bits, record_index);
				wordop_bitclear_at(snap_buffer->record_saved_bits, record_index);
				result = TRUE;
			}
			if(snap_buffer->record_updated_bits == 0U) {
				snap_buffer->buffer_acquired = FALSE;
			}
		}
	}
	if(snap_buffer_failed != NULL) {
		*snap_buffer_failed = buffer_request_failed;
	}
	return result;
}

bool pldtc_store_snapshot_to_eeprom(uint8 dtc_config_index, uint8 record_index, uint8* ram_buffer) {
	PlDtcInfo *dtc_info = &(pldtc_info[dtc_config_index]);
	uint16 snap_record_offset = dtc_info->snap_record_offset + record_index;
	uint16 snap_eep_address_offset = dtc_info->snap_eep_address_offset +  pldtc_snapshot_record_status_cache[snap_record_offset].record_data_offset;
	uint16 snap_eep_size = pldtc_snapshot_record_status_cache[snap_record_offset].record_data_size;
	bool write_result;
	snap_eep_size += EEP_BYTE_COUNT_FOR_MARK_CRC;

	write_result = pleep_write_back_data_with_offset(pldtc_eep_snapshot_config_index, snap_eep_address_offset, snap_eep_size, ram_buffer);
	return write_result;
}

bool pldtc_load_snapshot_from_eeprom_by_record_number(uint8 dtc_config_index, uint8 record_number, uint8* ram_buffer, uint16 *filled_length, bool should_fill_record_number) {
	const PlDtcConfig *dtc_config = &(pldtc_dtc_config[dtc_config_index]);
	uint8 record_index;
	uint8 end_record_index = pldtc_info[dtc_config_index].snap_record_count;
	uint16 one_record_length;
	uint16 total_record_length = 0U;
	bool result = true;
	uint16 previous_record_number = 0xFFU;
	uint8 *dtc_count_ram;
	uint8 did_count;

	for(record_index = 0x00U; record_index < end_record_index; record_index++) {
		uint16 target_record_number = pldtc_get_record_number_from_dtc_config(dtc_config, record_index);
		if((record_number == RECORD_NUMBER_FOR_ALL) || (target_record_number == record_number)) {
			bool treat_as_new_record = ((should_fill_record_number == false) || (previous_record_number != target_record_number));
			bool local_should_fill_record_number = false;
			if(treat_as_new_record) {
				local_should_fill_record_number = should_fill_record_number;
				dtc_count_ram = &(ram_buffer[1]);
			}
			result &= pldtc_load_snapshot_from_eeprom_by_record_index(dtc_config_index, record_index, ram_buffer, &one_record_length, &did_count, local_should_fill_record_number);
			if(!treat_as_new_record) { /*append to previous record*/
				*dtc_count_ram += did_count;
			}

			total_record_length += one_record_length;
			ram_buffer += one_record_length;

			previous_record_number = target_record_number;
		}
	}
	if(filled_length != NULL) {
		*filled_length = total_record_length;
	}
	return result;
}

bool pldtc_load_snapshot_identification_by_record_number(uint8* ram_buffer, uint16* filled_length) {
	bool result = true;
	const PlDtcConfig *dtc_config;
	PlDtcInfo *dtc_info;

	uint8 dtc_index;
	uint8 record_index;
	uint8 end_record_index;
	PlDtcStatusBit dtc_status;
	uint16 total_record_length = 0x00U;
	for(dtc_index = 0U; dtc_index < pldtc_config_count; dtc_index++) {
		uint8 previous_record_number = 0xFFU;

		dtc_info = &(pldtc_info[dtc_index]);
		dtc_config = &(pldtc_dtc_config[dtc_index]);
		dtc_status = pldtc_eep_mirror[dtc_index].dtc_status.status_bits;
		if(dtc_info->enabled && dtc_status.confirmedDTC) {
			end_record_index = dtc_info->snap_record_count;
			for(record_index = 0x00U; record_index < end_record_index; record_index++) {
				uint8 target_record_number = pldtc_get_record_number_from_dtc_config(dtc_config, record_index);
				if(target_record_number != previous_record_number) {
					(void)pldtc_fill_dtc_code_to_buffer_from_config(dtc_index, ram_buffer, false);
					ram_buffer[3] = target_record_number;
					ram_buffer += 4U;
					total_record_length += 4U;

					previous_record_number = target_record_number;
				}
			}

		}
	}
	if(filled_length != NULL) {
		*filled_length = total_record_length;
	}
	return result;
}


bool pldtc_load_snapshot_from_eeprom_by_record_index(uint8 dtc_config_index, uint8 record_index, uint8* ram_buffer, uint16 *filled_length, uint8* did_count_ptr, bool should_fill_record_number) {
	/*TODO: should load from cached memory when available*/
	const PlDtcConfig *dtc_config = &(pldtc_dtc_config[dtc_config_index]);
	const PlDidRecord *dtc_record_list = dtc_config->record_list;
	PlDtcInfo *dtc_info = &(pldtc_info[dtc_config_index]);
	bool load_result = FALSE;
	uint8 *dest_buffer = ram_buffer;
	uint16 flength = 0U;
	uint8 record_number = pldtc_get_record_number_from_dtc_config(dtc_config, record_index);
	uint8 did_count = 0x00U;
	if(record_index != INVALID_RECORD_INDEX) {
		uint16 snap_record_offset = dtc_info->snap_record_offset + record_index;
		uint16 snap_eep_address_offset = dtc_info->snap_eep_address_offset + pldtc_snapshot_record_status_cache[snap_record_offset].record_data_offset;
		uint16 snap_eep_size = pldtc_snapshot_record_status_cache[snap_record_offset].record_data_size;
		flength += pldtc_snapshot_record_status_cache[snap_record_offset].record_data_size;
		snap_eep_size += EEP_BYTE_COUNT_FOR_MARK_CRC;
		did_count = pldtc_snapshot_record_status_cache[snap_record_offset].record_did_count;
		if(should_fill_record_number) {
			dest_buffer[0] = record_number;
			dest_buffer[1] = did_count;
			dest_buffer += 2U;
			flength += 2U;
		}
		load_result = pleep_load_to_ram_with_offset(pldtc_eep_snapshot_config_index, snap_eep_address_offset, snap_eep_size, (uint8 *)dest_buffer, TRUE);
		if(!load_result) {
			(void)pldid_read_did_record(dtc_record_list[record_index], FALSE, TRUE, dest_buffer, TRUE);
		}
	}
	if(filled_length != NULL) {
		*filled_length = flength;
	}
	if(did_count_ptr != NULL) {
		*did_count_ptr = did_count;
	}
	return load_result;
}


void pldtc_save_snapshot_task(void) {
	bool saved;
	saved = pldtc_save_snapshot();
	if(!saved) {
		(void)pldtc_clear_diag();
	}
}

bool pldtc_save_snapshot(void) {
	uint8 indexi, indexj;
	PlDtcSnapBuffer *snap_buffer;
	PlDtcSnapBuffer *target_snap_buffer;
	PlDtcInfo *dtc_info;
	uint8 dtc_config_index;
	bool buffer_acquired;
	uint8 record_count;
	uint8 should_save_eep_bits;

	uint8 target_record_index = INVALID_RECORD_INDEX;
	uint8* target_ram;
	bool saved = FALSE;
	for(indexi = 0U; indexi < SNAP_BUFFER_COUNT; indexi++) {
		snap_buffer = &(pldtc_snap_buffer_pool[indexi]);
		dtc_config_index = snap_buffer->assigned_dtc_config_index;
		buffer_acquired = snap_buffer->buffer_acquired;
		should_save_eep_bits = ((snap_buffer->record_updated_bits) & (uint16)(~(snap_buffer->record_saved_bits)));
		if(buffer_acquired && (dtc_config_index != INVALID_DTC_CONFIG_INDEX)) {
			dtc_info = &(pldtc_info[dtc_config_index]);
			if(dtc_info->eep_clear_state == DTC_EEP_CLEAR_REQUESTED) {
				snap_buffer->buffer_acquired = FALSE; /*The snapshot will be cleared later, so just release the buffer*/
			} else if(target_record_index == INVALID_RECORD_INDEX) {
				record_count = dtc_info->snap_record_count;
				for(indexj = 0U; indexj < record_count; indexj++) {
					if(wordop_bittest_at(should_save_eep_bits, indexj)) {
						target_record_index = indexj;
						target_snap_buffer = snap_buffer;
						target_ram = target_snap_buffer->data + pldtc_snapshot_record_status_cache[dtc_info->snap_record_offset + indexj].record_data_offset;
						break ;
					}
				}
				if(target_record_index != INVALID_RECORD_INDEX) {
					break ;
				}
			}
		}
	}
	if(target_record_index != INVALID_RECORD_INDEX) {
		(void)pldtc_store_snapshot_to_eeprom(dtc_config_index, target_record_index, target_ram);
		wordop_bitset_at(target_snap_buffer->record_saved_bits, target_record_index);
		should_save_eep_bits = ((target_snap_buffer->record_updated_bits) & (uint16)(~(target_snap_buffer->record_saved_bits)));
		if(should_save_eep_bits == 0U) { /*TODO*/
			if(dtc_info->eep_clear_state == DTC_EEP_STATUS_CLEARED) {
				dtc_info->eep_clear_state = DTC_EEP_SNAP_CLEARED; /*all snap records are cleared*/
			}
			target_snap_buffer->buffer_acquired = FALSE; /*release this buffer when all records are saved*/
		}
		saved = TRUE;
	}
	return saved;
}

uint8 pldtc_get_dtc_status(uint32 dtc_code) {
	uint8 dtc_config_index = pldtc_find_dtc_config_index(dtc_code);
	uint8 dtc_status = 0U;
	if(dtc_config_index != INVALID_DTC_CONFIG_INDEX) {
		dtc_status = pldtc_get_dtc_status_for_config(dtc_config_index);
	}
	return dtc_status;
}

uint16 pldtc_get_dtc_count_and_fill_dtc_code_by_status_mask(uint8 status_mask, uint8* dest_buffer, bool all_dtc) {
	uint16 count = 0U;
	uint8 index;
	uint8 dtc_status;
	for(index = 0U; index < pldtc_config_count; index++) {
		dtc_status = pldtc_get_dtc_status_for_config(index);
		/*Test Case: DS19Phy_PositiveResponse Delete the status_mask == 0*/
		if(pldtc_info[index].enabled && (((dtc_status & status_mask) != 0U)||all_dtc)) {
			count++;
			if(dest_buffer != NULL) {
				uint8 filled_length = pldtc_fill_dtc_code_to_buffer_from_config(index, dest_buffer, true);
				dest_buffer += filled_length;
			}
		}
	}
	return count;
}


uint8 pldtc_fill_dtc_code_to_buffer_from_config(uint8 dtc_config_index, uint8 *buffer, bool should_fill_status) {
	uint8 filled_length = 0x03U;
	uint32 dtc_code = pldtc_dtc_config[dtc_config_index].dtc_code;
	buffer[0U] = ((dtc_code >> 16U) & 0xFFU);
	buffer[1] = ((dtc_code >> 8U) & 0xFFU);
	buffer[2] = ((dtc_code >> 0U) & 0xFFU);
	if(should_fill_status) {
		filled_length++;
		buffer[3] = pldtc_get_dtc_status_for_config(dtc_config_index);
	}
	return filled_length;
}

uint8 pldtc_get_dtc_status_for_config(uint8 dtc_config_index) {
	return (pldtc_eep_mirror[dtc_config_index].dtc_status.dtc_status & DTC_STATUS_AVAILABLE_MASK);
}

void pldtc_supress_dtc_updating(bool should_supress_updating) {
	pldtc_suppress_updating_dtc = should_supress_updating;
}

void pldtc_monitor_task(void) {

}


#endif


